import React, {Component} from 'react';
import CanvasSvg from "./canvasSVG";
import {getTree} from "./getTree";
import Menu from "./menu";
import Navbar from "./navbar";
import {MinHeap} from "./Heap";
import $ from "jquery"
import {graph_random, r, randomList, svgHeight, svgWidth, tree_random, xBias, xTimes, yBias, yTimes} from "./Const";
import CustomizedTables from "./table"
import GRAPH from "./help/GRAPH";
import TableTreeGraph from "./tableTreeGraph";
import TableForPrim from "./tableForPrim";
import TableForKruskal from "./tableForKruskal";


let tree;
let before = false;
let pause = false;
let reset = false;
// s 是字符串
const checkNumber = (s) => {
    return Number(s, 10).toString() === s;
}

class ForceEnum extends Component {
    constructor() {
        super();
        this.state = {
            reset: false,
            vertices: [],
            edges: [],
            current: -1,
            nrFlag: true,
            redNumber: -1,
            algo: 0,
            animation_stop: true,
            time: 500,
            leftMost: undefined,
            rightMost: undefined,
            upMost: undefined,
            downMost: undefined,
            width: undefined,
            height: undefined,
            // 随机选择的num
            selectNum: 0,
            // 最多可以选择到多少N
            MaxN: 11,

            select: false,

            setGreenFlag: false,


            // 图
            changed: false,
            // 关于 dijkstra table
            from: 0,
            nowSelectedNode: -1,
            // 0 代表 selected， 1 代表 visiting， 2 代表 visited
            edgeVisitState: -1,
            edgeIndex: -1,
            repaint: false,

            selectDirect: false,

            nodes: [],
            nowVisitedOrder:[],
            nowVisitedNodes:[],
            visited: {},
            nowIndex:-1,

            // 1 前序遍历 2 中序遍历 3 后序遍历
            type: 1,
            // 中后遍历需要的
            color: "white"
        }

        this.nodes = [];
        this.edges = [];
        this.directedGraph = {};
        this.vertexReflection = {}
        this.input = [];
    }


    handlePaused = (flag) => {
        // this.setState({isPaused: flag});
        before = pause;
        pause = flag;
    }

    handlePausedForLoop = () => {
        before = false;
        pause = true;
    }

    handleDirect = async (select) => {
        await this.realOnReset();
        this.setState({selectDirect: select});
        await sleep(1);
        await this.handleDraw();

    }

    findNodeIndex = (val) => {

        for (let i in this.state.nodes) {
            if (this.nodes[i].value === val) {
                return i;
            }
        }
    }

    handleFrom = (val) => {
        this.setState({from: val});
    }

    handleGenerate = async () => {
        let inputElement;
        await this.realOnReset();
        let algo = this.state.algo;
        await this.realOnReset();

        await this.setCustomState({reset: false});
        reset = false;
        if (algo !== 0 && algo !== 1) {
            inputElement = $("#input-tree");
            inputElement.val(tree_random[Math.floor(Math.random() * tree_random.length)]);

        } else {
            inputElement = $("#input");
            let random = Math.floor(Math.random() * graph_random.length);
            inputElement.val(graph_random[random])

        }
        await this.handleDraw();
        await this.setCustomState({reset: true});
        reset = true;

    }

    handleSpeed = (speed) => {
        let time;
        if (speed >= 80) {
            time = 900 - 9 * speed + 1;
        }
        else {
            time = 1800 - 8 * speed;
        }
        this.setState({time})
    }

    changeSelect = () => {
        let select = !this.state.select;
        this.setState({select});
    }

    realOnReset = async () => {
        pause = true;

        this.changeSelect();
        this.onReset();
        this.setState({changed: !this.state.changed});

        await sleep(1);
    }

    onReset = () => {
        pause = true;

        this.setState({reset: true, setGreenFlag: false,
            // 关于 dijkstra table

            nowSelectedNode: -1,
            // 0 代表 selected， 1 代表 visiting， 2 代表 visited
            edgeVisitState: -1,
            edgeIndex: -1,
            changed: false,

            repaint: false,

            // selectDirect: false,


            nowVisitedOrder:[],
            nowVisitedNodes:[],

            nowIndex:-1,
            // 1 前序遍历 2 中序遍历 3 后序遍历
            type: 1,
            // 中后遍历需要的
            color: "white",
            current:-1


        });

        reset = true;

        $("#img").css({
                        scale: "1",
                        transform: "scale(1)",
                        left: "0",
                        top: "10px",
                      });
        pause = before = false;
    }

    // 执行去除tab和空格的操作
    clearPure = (string) => {
        let final = "";
        for (let i = 0; i < string.length; i++) {
            if (string[i] !== " " || string[i] !== "\t")
                final += string[i];
        }
        return final;
    }

    clearTabSpace = (string) => {
        let final = this.clearPure(string);
        return final.split(",").map((value) => parseInt(value));
    }

    sort = (string) => {
        return string.sort((a,b) => a-b);
    }

    handleDraw = async () => {
        await this.handleInput();
        await sleep(1);

        this.setState({changed: !this.state.changed});
    }

    handleVertexEdges = (array, nodes, edges) => {
        let temp = [...array];
        temp.splice(0, 1);
        let total = {};

        const inArray = (point, total, nodes) => {
            if (!(point in total)) {
                total[point] = 1;
                nodes.push({
                    id: `node${point}`,
                    value: point,
                    label: `V${point}`
                });
            }
        }

        let directedGraph = {};
        const checkEdgeNotIn = (src, dst, edges)=>{
            for (let i = 0; i < edges.length; i++) {
                if ((`node${src}` === edges[i].source && `node${dst}` === edges[i].target)
                || (`node${dst}` === edges[i].source && `node${src}` === edges[i].target) )
                    return false;
            }
            return true;

        };

        for(let item of temp) {
            let src = item[0];
            let dst = item[1];
            let weight = item[2];
            inArray(src, total, nodes);
            inArray(dst, total, nodes);
            if (this.state.selectDirect) {
                if (checkEdgeNotIn(src, dst, edges))
                edges.push({
            source: `node${src}`,
            target: `node${dst}`,
            label: String(weight),
        });
            } else {
                edges.push({
            source: `node${src}`,
            target: `node${dst}`,
            label: String(weight),
        });
            }
        }

        for (let i = 0; i < edges.length; i++) {
            let src = parseInt(edges[i].source.substring(4));
            let dst = parseInt(edges[i].target.substring(4));
            let weight = parseInt(edges[i].label);
             if (!(dst in directedGraph)) {
                directedGraph[dst] = [];
            }

            if (!(src in directedGraph)) {
                directedGraph[src] = [];
            }

            if (this.state.selectDirect)
                directedGraph[dst].push([src,weight, i]);
            directedGraph[src].push([dst, weight, i]);

        }

        directedGraph = {...directedGraph};
        this.directedGraph = directedGraph;
    }


    handleGraphInput = async (value) => {
        let valueArray = value.split("\n");
        valueArray = valueArray.filter(x=>x.length > 0);
        valueArray = valueArray.map(x=>x.split(/[\t ]+/).map(x=>parseInt(x)));

        let vertexCount = valueArray[0][0];
        let edgeCount = valueArray[0][1];
        let nodes = [];
        let edges = [];
        this.handleVertexEdges(valueArray, nodes, edges);
        nodes.sort((a,b)=>a.value-b.value);
        await this.setCustomState({nodes}, 1);
        this.nodes = [...nodes];
        this.edges = [...edges];
        await sleep(1);
    }

    shiftPosition = async (tree) => {
        this.transForDraw(tree, tree.x);

        let leftMost = tree.getMostLeft(tree).x;
        if (leftMost - 2 * r < 0) {
            this.transForLeftX(tree, -leftMost + 2 * r);
        }

        leftMost = tree.getMostLeft(tree).x;

        let rightMost = tree.getMostRight(tree).x;
        let upMost = tree.y;
        let downMost = tree.getMostY(tree);
        let width = rightMost - leftMost + 4 * r;
        let height = downMost - upMost + 4 * r;

        await this.setCustomState({
            width,
            height,
            }, 1);
    }

    handleTreeInput = async (value) => {
        let valueArray = value.split("\n");
        valueArray = valueArray.filter(x=>x.length > 0);
        valueArray = valueArray.map(x=>x.split(/[\t ]+/).map(x=>parseInt(x)));

        tree = getTree(valueArray);

        await this.shiftPosition(tree);
        let vertices = [];
        let edges = [];
        const recur = (node,parent)=>{
            // let current = vertices.length;
             if( parent ){

                if( node.left || node.right )
                    vertices.push({label:node.tree.label,x:node.x,y:node.y,px:parent.x,py:parent.y});
                else
                    vertices.push({label:node.tree.label,x:node.x,y:node.y,px:parent.x,py:parent.y});
                edges.push({
                    x1:parent.x,
                    y1:parent.y,
                    x2:node.x,
                    y2:node.y
                });
            } else {
                if( node.left || node.right)
                    vertices.push({label:node.tree.label,x:node.x,y:node.y,px:node.x,py:node.y});
                else
                    vertices.push({label:node.tree.label,x:node.x,y:node.y,px:node.x,py:node.y});
            }

            if (node.left) {
                recur( node.left, node);
            }
            if (node.right) {
                recur( node.right, node);
            }
    }

        recur(tree, undefined);

        for (let i = 0; i < vertices.length; i++) {
            this.vertexReflection[vertices[i].label] = i;
        }
        await this.setCustomState({vertices, edges});
    }

    handleInput = async () => {
        try {
            if (this.state.algo in [0, 1]) {
                let inputElement = document.getElementById("input");
                await this.handleGraphInput(inputElement.value);
            } else {
                let inputElement = document.getElementById("input-tree");
                await this.handleTreeInput(inputElement.value);
            }
        } catch (e) {

        }

    }


    setAlgo = async (pos, val) => {
        if (pos === 0) {
            await this.realOnReset();
            this.setState({algo: val, nrFlag: val === 0});

            // this.setState({selectDirect: false});
            await sleep(1);
            await this.handleDraw();


        }
    }

    setN = (pos, val) => {
        if (pos === 0) {
            this.setState({n: val});
        }
    }

    setR = (pos, val) => {
        if (pos === 0) {
            this.setState({r: val});
        }
    }
    addNumber = async ()=>{
        if (before) {

        }
        else {
        this.onReset();
        if (this.state.algo in [0, 1]) {
                this.setState({repaint: !this.state.repaint})
            }
        else {

        }

        await this.addNumberTemp();
        this.changeSelect();
        pause = before = false;
        }


    }

    transForDraw = (tree, rootBias) => {
        tree.x = (tree.x - rootBias) * xTimes;
        tree.y = tree.y * yTimes + yBias;
        if (tree.left) {
            this.transForDraw(tree.left, rootBias);
        }
        if (tree.right) {
            this.transForDraw(tree.right, rootBias);
        }
    }

    transForLeftX = (tree, bias) => {
        tree.x += bias;
        if (tree.left) {
            this.transForLeftX(tree.left, bias);
        }
        if (tree.right) {
            this.transForLeftX(tree.right, bias);
        }
    }

    addNumberTemp = async () => {
            let algo = this.state.algo;
            this.setState({reset: false});
            reset = false;
            if (!(algo in [0, 1])) {

                if (algo === 2) {
                    await this.preorder();
                } else if (algo === 3) {
                    await this.inorder();
                } else if (algo === 4) {
                    await this.postorder();
                } else {
                    await this.layerOrder();
                }
            }
            else {
                // await this.handleInput();
                this.changeSelect();
                    this.handlePausedForLoop();
                if (algo === 0) {
                    await this.dfsTraversal(this.state.from);
                } else if (algo === 1) {
                    await this.bfsTraversal(this.state.from);
                }
            }
    }

    prepareTree = async () => {
        await this.setCustomState({visited: {}});
        let visited = {};
        let vertices = [...this.state.vertices];
        for (let i = 0; i < vertices.length; i++) {
            visited[vertices[i].label] = false;
        }
        await this.setCustomState({current: -1, visited});

        await this.setCustomState({nowVisitedOrder: this.generateN(this.state.vertices.length, 1)});
        return visited;
    }

    preorder = async () => {
        let visited = await this.prepareTree();
        let nodes = [];
        await this.setCustomState({type: 1});
        let count = 0;
        const pre = async (node) => {
            if (node) {
                nodes.push(node.tree.label);
                let current = this.vertexReflection[node.tree.label];
                await this.setCustomState({current, visited: {...visited}}, this.state.time);

                // 表格的逻辑
                {
                    await this.setCustomState({nowVisitedNodes: [...nodes]}, this.state.time);
                    await this.setCustomState({nowIndex: count++}, this.state.time);
                }

                visited[node.tree.label] = true;

                await pre(node.left);
                if (node.left && count < this.state.vertices.length)
                    await this.setCustomState({current, visited: {...visited}}, this.state.time);


                await pre(node.right);
                if (node.right && count < this.state.vertices.length)
                    await this.setCustomState({current, visited: {...visited}}, this.state.time);

            }
        }
        await pre(tree);
    }

    inorder = async () => {
        let visited = await this.prepareTree();
        let nodes = [];
        await this.setCustomState({type:2});
        let count = 0;
        const mid = async (node) => {
            if (node ) {
                let current = this.vertexReflection[node.tree.label];
                if (node.left && count < this.state.vertices.length)
                    await this.setCustomState({current, color: 'lightyellow'}, this.state.time);
                await mid(node.left);

                nodes.push(node.tree.label);

                await this.setCustomState({current, color: 'lightgreen', visited}, this.state.time);
                // 表格的逻辑
                {
                    await this.setCustomState({nowVisitedNodes: [...nodes]}, this.state.time);
                    await this.setCustomState({nowIndex: count++}, this.state.time);
                }
                visited[node.tree.label] = true;


                await mid(node.right);
                if (node.right && count < this.state.vertices.length)
                    await this.setCustomState({current, color: 'lightyellow'}, this.state.time);
                else
                    await sleep(this.state.time);
            }
        }
        await mid(tree);
    }

    postorder = async () => {
        let visited = await this.prepareTree();
        let nodes = [];
        await this.setCustomState({type:2});
        let count = 0;
        const post = async (node) => {
            if (node) {
                let current = this.vertexReflection[node.tree.label];
                if (node.left)
                    await this.setCustomState({current, color: 'lightyellow'}, this.state.time);
                await post(node.left);
                if (node.right)
                    await this.setCustomState({current, color: 'lightyellow'}, this.state.time);
                else
                    await sleep(this.state.time);
                await post(node.right);

                nodes.push(node.tree.label);
                await this.setCustomState({current, color: 'lightgreen', visited}, this.state.time);
                // 表格的逻辑
                {
                    await this.setCustomState({nowVisitedNodes: [...nodes]}, this.state.time);
                    await this.setCustomState({nowIndex: count++}, this.state.time);
                }
                visited[node.tree.label] = true;
            }
        }
        await post(tree);
    }

    layerOrder = async () => {
        let visited = await this.prepareTree();
        let nodes = [];
        await this.setCustomState({type:2});
        let count = 0;
        const layer = async (node) => {
            let queue = [];
            queue.push(node);
            while (queue.length !== 0) {
                let [node] = queue.splice(0, 1);
                // 处理一个点
                let current = this.vertexReflection[node.tree.label];
                nodes.push(node.tree.label);
                await this.setCustomState({current, color: 'lightgreen', visited}, this.state.time);
                // 表格的逻辑
                {
                    await this.setCustomState({nowVisitedNodes: [...nodes]}, this.state.time);
                    await this.setCustomState({nowIndex: count++}, this.state.time);
                }
                visited[node.tree.label] = true;

                // 处理后代
                if (node.left)
                    queue.push(node.left);
                if (node.right)
                    queue.push(node.right);
            }


        };
        await layer(tree);
    }

    stateAwait = async () => {
        await sleep(1);
    }

    setCustomState = async (d, mode=0) => {
        this.setState(d);
        if (mode === 0)
            await this.stateAwait();
        else
            await sleep(mode);
    }

    bfsTraversal  = async (from) => {
        let [nodes, edges, at, visited, visitedEdge] = this.getNodesEdges(from);
        let nowVisitedNodes = [];
        if (nodes.length === 0 || edges.length === 0)
            return;
        let bfs = async (at)=> {
            let queue = [];
            queue.push(at);
            while (queue.length > 0) {
                // 处理 队头
                let [begin] = queue.splice(0, 1);
                let order = this.findNodeIndex(begin);
                if (!visited[begin]) {
                    await this.processNodes(begin, visited, nowVisitedNodes);
                }
                let relation = this.directedGraph[begin];
                for (let array of relation) {
                    let order = this.findNodeIndex(array[0]);
                    let edgeIndex = array[2];
                    if (!visitedEdge[edgeIndex]) {
                        await this.setCustomState({edgeVisitState: 1, edgeIndex}, this.state.time);
                        visitedEdge[edgeIndex] = true;

                        if (!visited[nodes[order]]) {
                            queue.push(array[0]);
                            await this.setCustomState({edgeVisitState: 0, edgeIndex}, this.state.time);
                            await this.processNodes(array[0], visited, nowVisitedNodes);
                        } else {
                            await this.setCustomState({edgeVisitState: 2, edgeIndex}, this.state.time);
                        }
                    }
                }

            }
        }
        await this.setCustomState({nowVisitedOrder: this.generateN(this.nodes.length, 1),}, this.state.time);

        await bfs(at);

        for (let i in this.directedGraph) {
            i = parseInt(i);
            if (!visited[i]) {
                console.log("now I:", i);
                await bfs(i);
            }
        }
        console.log(this.directedGraph)
        console.log(visited)
    }

    processNodes = async (at, visited, nowVisitedNodes) => {

        let order = this.findNodeIndex(at);
        visited[at] = true;
        await this.setCustomState({nowSelectedNode: order}, this.state.time);
        // if (nowVisitedNodes.length !== this.nodes.length) {
            nowVisitedNodes.push("V" + at);
        await this.setCustomState({nowVisitedNodes: [...nowVisitedNodes]}, this.state.time);
        await this.setCustomState({nowIndex: nowVisitedNodes.length - 1}, this.state.time);
        // }

    }

    getNodesEdges = (from) => {
        let nodes = [...this.nodes].map(x=>x.value);
        let edges = [...this.edges];
        let at = nodes[from];
        let visited = new Map();
        for (let i in nodes) {
            visited[nodes[i]] = false;
        }
        let visitedEdge = new Map();
        for (let i in edges) {
            visitedEdge[i] = false;
        }
        return [nodes, edges, at, visited, visitedEdge]
    }

    /**
     * at = nodes[from] 代表实际的数字
     * @param from, from 序列标号，属于nodes的第几个
     * @returns {Promise<void>}
     */
    dfsTraversal = async (from) => {


        let [nodes, edges, at, visited, visitedEdge] = this.getNodesEdges(from);
        let nowVisitedNodes = [];

        if (nodes.length === 0 || edges.length === 0)
            return;
        let dfs = async (at)=> {

            await this.processNodes(at, visited, nowVisitedNodes);

            // relation = [[dst, weight, edgeIndex] * length]
            let relation = this.directedGraph[at];
            for (let array of relation) {
                let order = this.findNodeIndex(array[0]);
                let edgeIndex = array[2];
                if (!visitedEdge[edgeIndex]) {
                    await this.setCustomState({edgeVisitState: 1, edgeIndex}, this.state.time);
                    visitedEdge[edgeIndex] = true;

                    if (!visited[nodes[order]]) {
                        await this.setCustomState({edgeVisitState: 0, edgeIndex}, this.state.time);
                        await dfs(array[0]);
                    } else {
                        await this.setCustomState({edgeVisitState: 2, edgeIndex}, this.state.time);
                    }
                }
            }


        }
        await this.setCustomState({nowVisitedOrder: this.generateN(this.nodes.length, 1),}, this.state.time);
        await dfs(at);
        for (let i in this.directedGraph) {
            i = parseInt(i);
            if (!visited[i]) {
                console.log("now I:", i);
                await dfs(i);
            }
        }
    }

    generateN = (n, bias=0) => {
        let list = [];
        for (let i = 0; i < n; i++) {
            list.push(i + bias);
        }
        return list;
    }

    algoCast = (algo) => {
        switch (algo) {
            case 0: return "DFS";
            case 1: return "BFS";
            case 2: return "Preorder Traversal";
            case 3: return "Inorder Traversal";
            case 4: return "Postorder Traversal";
            case 5: return "Level Traversal";
        }
    }

    algoDisplay = () => {
        let display;

        if (!(this.state.algo in [0, 1])) {
            display =  (
                <React.Fragment>
                    <CanvasSvg

                    width = {this.state.width}
                    height = {this.state.height}

                    vertices = {this.state.vertices}
                    edges = {this.state.edges}
                    current={this.state.current}
                    reset = {this.state.reset}
                    visited = {this.state.visited}
                    type = {this.state.type}
                    color = {this.state.color}
                />
                            <TableTreeGraph
                    listN={this.state.nowVisitedOrder}
                    tableName = {this.algoCast(this.state.algo) }
                    nowIndex = {this.state.nowIndex}
                    nodes={this.state.nowVisitedNodes}
                    reset = {reset}
            />


                </React.Fragment>
            )
        } else {
            let table =(
                <TableTreeGraph
                    listN={this.state.nowVisitedOrder}
                    tableName = {this.algoCast(this.state.algo)}
                    nowIndex = {this.state.nowIndex}
                    nodes={this.state.nowVisitedNodes}
                    reset = {reset}
            />);
            display = (
                <div className="row" style={{height: "calc(100vh - 120px)"}}>
                    <div className="col-6 left-panel d-flex justify-content-center align-items-center" id="left-panel">
                        <GRAPH
                        nodes = {this.nodes}
                        edeges = {this.edges}
                        changed = {this.state.changed}
                        nowSelectedNode = {this.state.nowSelectedNode}
                        edgeVisitState = {this.state.edgeVisitState}
                        edgeIndex = {this.state.edgeIndex}
                        repaint = {this.state.repaint}
                        reset = {reset}
                        selectDirect = {this.state.selectDirect}
                        algo = {this.state.algo}
                        />
                    </div>

                    <div className="col-6 border-end p-3 align-self-center" id="right-panel">
                        <div className="mt-3">
                            {table}
                        </div>
                    </div>
                </div>
            )

    //        $(function(){
    //            $("#dijkstraTable").draggable({containment: "body"});
    // });

        }

        return display;
    }


    render = () =>  {
        // $('body').css({"overflow-x": "hidden"});
        return (
            <div style={{
                overflow: "hidden",
                }}>
                <Navbar
                    name={"Force Enum"}
                />
                <Menu
                    handlePaused = {this.handlePaused}
                    select = {this.state.select}

                    onChange = {this.realOnReset}

                    handleDirect = {this.handleDirect}

                    MaxN = {this.state.MaxN}
                    selectNum = {this.state.selectNum}
                    onGenerate = {this.handleGenerate}
                    algo={this.state.algo}
                    handleSpeed = {this.handleSpeed}
                    setN={this.setN}
                    setR={this.setR}
                    setAlgo={this.setAlgo}
                    onStart={this.addNumber}
                    onReset={this.realOnReset}
                    // 图
                    handleDraw = {this.handleDraw}

                    // 给dijkstra 提供的
                    handleFrom = {this.handleFrom}
                    from = {this.state.from}
                    nodes = {this.state.nodes}
                />

                {this.algoDisplay()}

            </div>
        );
    }
}

// const requestAnimationFrame = window.requestAnimationFrame
// || window.mozRequestAnimationFram
// || window.webkitRequestAnimationFrame
// || window.msRequestAnimationFrame
// || function(callback){ window.setTimeout(callback, 1000/ 60)}

const cancelAnimationFrame = window.cancelAnimationFrame
|| window.mozCancelAnimationFrame;

let allRef = null;

function animateTimeout(cb,time){

	let i = 0;

    let myRef = null;
    let rememberTime = time;
	myRef = requestAnimationFrame(function fn(){
        if (pause)
            time = 100000000000;
        else
            time = rememberTime;
		if(i > Math.floor(60 / (1000/time)) || reset){
			cb();
			cancelAnimationFrame(myRef);
            // return ;
		}
		i++;

		myRef = requestAnimationFrame(fn)
	})
}


function sleep(ms) {
    return new Promise(resolve => animateTimeout(resolve, ms));
}
export default ForceEnum;